package githttpbackend

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"net/http/cgi"
	"strconv"
	"sync"

	"gopkg.in/yaml.v2"

	"gitee.com/banggua/ggtsync/config"
	"gitee.com/banggua/ggtsync/logger"
)

const (
	HandlerName = "githttpbackend"
)

type gitHttpBackendClient struct {
	locker  *sync.Mutex
	Servers map[string]*http.Server
}

var (
	DefaultGitHttpBackendClient = &gitHttpBackendClient{
		locker:  &sync.Mutex{},
		Servers: make(map[string]*http.Server),
	}
)

func (ghbc *gitHttpBackendClient) Test(project *config.Project) error {
	if len(project.Args) != 4 {
		logger.Errorf("%v", errors.New("invalid arguments"))
		return errors.New("invalid arguments")
	}
	// 1. git_http_backend_port
	if _, exist := project.Args["git_http_backend_port"]; !exist {
		logger.Errorf("%v", errors.New("args.git_http_backend_port field does not exist"))
		return errors.New("args.git_http_backend_port field does not exist")
	} else if port, err := strconv.Atoi(project.Args["git_http_backend_port"]); err != nil || port < 0 || port > 65535 {
		logger.Errorf("%v", errors.New("args.git_http_backend_port field is invalid"))
		return errors.New("args.git_http_backend_port field  is invalid")
	}
	// 2. git_http_backend_path
	if _, exist := project.Args["git_http_backend_path"]; !exist {
		logger.Errorf("%v", errors.New("args.git_http_backend_path field does not exist"))
		return errors.New("args.git_http_backend_path field does not exist")
	}
	// 3. git_http_backend_path
	if _, exist := project.Args["git_http_backend_path"]; !exist {
		logger.Errorf("%v", errors.New("args.git_http_backend_path field does not exist"))
		return errors.New("args.git_http_backend_path field does not exist")
	}
	// 4. git_project_root
	if _, exist := project.Args["git_project_root"]; !exist {
		logger.Errorf("%v", errors.New("args.git_project_root field does not exist"))
		return errors.New("args.git_project_root field does not exist")
	}
	return nil
}

func (ghbc *gitHttpBackendClient) Templet() string {
	var jiriYaml = &config.Project{
		Name:  "git-http-backend",
		Order: 1,
		Args: map[string]string{
			"git_http_backend_port":    "80",
			"git_http_backend_pattern": "/",
			"git_http_backend_path":    "/usr/lib/git-core/git-http-backend",
			"git_project_root":         "/root/fuchsia",
		},
		Type:     HandlerName,
		From:     "NO NEED",
		To:       "NO NEED",
		Interval: 0,
		Plugins: []config.Plugin{
			{Name: "plugin1", Order: 2, Args: map[string]string{"arg1": "val1", "arg2": "val2"}},
			{Name: "plugin2", Order: 3, Args: map[string]string{"arg1": "val1", "arg2": "val2"}},
		},
	}
	out, _ := yaml.Marshal(&jiriYaml)
	return string(out)
}

func (ghbc *gitHttpBackendClient) gitHttpBackendInit(project *config.Project, handler http.Handler) {
	var (
		gitHttpBackendPattern = project.Args["git_http_backend_pattern"]
		gitHttpBackendPort    = project.Args["git_http_backend_port"]
		gitHttpBackendAddr    = fmt.Sprintf(":%s", gitHttpBackendPort)
		mux                   = http.NewServeMux()
	)
	mux.Handle(gitHttpBackendPattern, handler)

	ghbc.locker.Lock()
	defer ghbc.locker.Unlock()
	ghbc.Servers[project.Name] = &http.Server{
		Addr:    fmt.Sprintf(":%s", gitHttpBackendAddr),
		Handler: mux,
	}
}

func (ghbc *gitHttpBackendClient) Handle(project *config.Project) error {
	var (
		gitHttpBackendPath = project.Args["git_http_backend_path"]
		gitProjectRoot     = project.Args["git_project_root"]
	)

	ghbc.gitHttpBackendInit(project, &cgi.Handler{
		Path: "/bin/sh",
		Args: []string{"-c", gitHttpBackendPath},
		Dir:  ".",
		Env: []string{
			fmt.Sprintf("GIT_PROJECT_ROOT=%v", gitProjectRoot),
			"GIT_HTTP_EXPORT_ALL=1",
		},
	})

	return ghbc.Servers[project.Name].ListenAndServe()
}

func (ghbc *gitHttpBackendClient) Close(project *config.Project) error {
	ghbc.locker.Lock()
	defer ghbc.locker.Unlock()
	if err := ghbc.Servers[project.Name].Shutdown(context.Background()); err != nil {
		return err
	}

	delete(ghbc.Servers, project.Name)
	return nil
}
